
/*
 * (a)  (C) 1990 by Adobe Systems Incorporated. All rights reserved.
 *
 * (b)  If this Sample Code is distributed as part of the Display PostScript
 *	System Software Development Kit from Adobe Systems Incorporated,
 *	then this copy is designated as Development Software and its use is
 *	subject to the terms of the License Agreement attached to such Kit.
 *
 * (c)  If this Sample Code is distributed independently, then the following
 *	terms apply:
 *
 * (d)  This file may be freely copied and redistributed as long as:
 *	1) Parts (a), (d), (e) and (f) continue to be included in the file,
 *	2) If the file has been modified in any way, a notice of such
 *      modification is conspicuously indicated.
 *
 * (e)  PostScript, Display PostScript, and Adobe are registered trademarks of
 *	Adobe Systems Incorporated.
 * 
 * (f) THE INFORMATION BELOW IS FURNISHED AS IS, IS SUBJECT TO
 *	CHANGE WITHOUT NOTICE, AND SHOULD NOT BE CONSTRUED
 *	AS A COMMITMENT BY ADOBE SYSTEMS INCORPORATED.
 *	ADOBE SYSTEMS INCORPORATED ASSUMES NO RESPONSIBILITY
 *	OR LIABILITY FOR ANY ERRORS OR INACCURACIES, MAKES NO
 *	WARRANTY OF ANY KIND (EXPRESS, IMPLIED OR STATUTORY)
 *	WITH RESPECT TO THIS INFORMATION, AND EXPRESSLY
 *	DISCLAIMS ANY AND ALL WARRANTIES OF MERCHANTABILITY, 
 *	FITNESS FOR PARTICULAR PURPOSES AND NONINFRINGEMENT
 *	OF THIRD PARTY RIGHTS.
 */

/*
 *	epsfwriter.c
 *
 *	Version:	2.0
 *	Author:	Ken Fromm
 *	History:
 *			03-07-91		Added this comment.
 */
 
#import <appkit/NXBitmapImageRep.h>
#import <appkit/color.h>
#import <appkit/graphics.h>
#import <appkit/nextstd.h>
#import <appkit/tiff.h>

#import <dpsclient/dpsclient.h>
#import <dpsclient/wraps.h>
#import <objc/List.h>
#import <objc/hashtable.h>
#import <streams/streamsimpl.h>
#import <streams/streams.h>

#import "ImportApp.h"
#import "epsfstruct.h"
#import "epsfwraps.h"

#define MAX_CHARS       255
#define MAX_BYTES       127

extern void		GetDocumentComment();
extern NXColor	GetColorAt(NXBitmapImageRep *image, int x, int y);
extern void		LogEpsfError();

/*
*	These arrays contain the proper instructions for including, initiating
*	and terminating procedure sets using the Adobe Illustrator format.
*/
const static char EpsfProcSetDef[ ] =
"% EPSF_Illustrator_abbrev
% Version 1.0 8/23/1990
% Copyright (C) 1987, 1988, 1989, 1990
% Adobe Systems Incorporated
% All Rights Reserved

userdict /EPSF_Illustrator_abbrev  14  dict dup begin put
% initialization
/initialize				% - initialize -
{
userdict /EPSF_Illustrator_abbrev_vars 1 dict dup begin put
/_i null def
EPSF_Illustrator_abbrev begin
EPSF_Illustrator_abbrev
	{
	dup xcheck
		{
		bind
		} if
	pop pop
	} forall
end
end
EPSF_Illustrator_abbrev begin
EPSF_Illustrator_abbrev_vars begin
newpath
} def

/terminate				% - terminate -
{
end
end
} def

/ddef				% key value ddef -
{
EPSF_Illustrator_abbrev_vars 3 1 roll put
} def

/npop				% integer npop -
{
	{
	pop
	} repeat
} def

% color operators
/g					% gray g -
{
setgray
} def

% graphic state operators
/d					% array phase d -
{
setdash
} def
/cf					% - cf flatness
currentflat def
/i					% flatness i -
{
dup 0 eq
	{
	pop cf
	} if
setflat
} def
/j					% linejoin j -
{
setlinejoin
} def
/J					% linecap J -
{
setlinecap
} def
/M					% miterlimit M -
{
setmiterlimit
} def
/w					% linewidth w -
{
setlinewidth
} def

% place operators
/`					% matrix llx lly urx ury string ` -
{
/_i save ddef
pop 5 -1 roll concat
newpath 4 2 roll  2 copy moveto
3 1 roll 1 index lineto
2 index exch lineto
lineto
closepath
clip newpath
userdict begin
/showpage {} def
} def

/~					% - ~ -
{
end
_i restore
} def

currentdict readonly pop end\n";

const static char EpsfProcSetInit[ ] = "EPSF_Illustrator_abbrev /initialize get exec\n";

const static char EpsfProcSetTerm[ ] = "EPSF_Illustrator_abbrev /terminate get exec\n";

void  WriteEpsfProcSetDef ()
{
	DPSWritePostScript(DPSGetCurrentContext(), EpsfProcSetDef, strlen(EpsfProcSetDef));
}

void  WriteEpsfProcSetInit ()
{
	DPSWritePostScript(DPSGetCurrentContext(), EpsfProcSetInit, strlen(EpsfProcSetInit));
}

void  WriteEpsfProcSetTerm ()
{
	DPSWritePostScript(DPSGetCurrentContext(), EpsfProcSetTerm, strlen(EpsfProcSetTerm));
}

/*
*	Writes the Adobe Illustrator 88 imported document operator.
*/
void  WriteEpsfIllustratorBeg(NXRect  *bounds, NXRect *boundsOrig, float rotation, char *filename)
{
	float			a = 1.0, b = 0.0, 
				c = 0.0, d = 1.0,
				sx = 1.0, sy = 1.0;

	NXPoint		t;

	DPSContext	ctxt;

	if (boundsOrig->size.width)
		sx = bounds->size.width/boundsOrig->size.width;
	if (boundsOrig->size.height)
		sy = bounds->size.height/boundsOrig->size.height;

	if (rotation != 0.0)
	{
		a = d = (float) cos(rotation);
		b = c = (float) sin(rotation);
		c = -c;
	}
	a = a * sx;
	b = b * sx;
	c = c * sy;
	d = d * sy;

	t.x = bounds->origin.x - boundsOrig->origin.x * sx;
	t.y = bounds->origin.y - boundsOrig->origin.y * sy;
	
	ctxt = DPSGetCurrentContext();
	DPSPrintf(ctxt, "\n0 g 0 i 0 J 0 j 1 w 10 M [] 0 d\n");
	DPSPrintf(ctxt, "[%g %g %g %g %g %g] %g %g %g %g", a, b, c, d, t.x, t.y,
		boundsOrig->origin.x, boundsOrig->origin.y,
		boundsOrig->origin.x + boundsOrig->size.width,
		boundsOrig->origin.y + boundsOrig->size.height);
	if (filename)
		DPSPrintf(ctxt, " (%s)", filename);
	else
		DPSPrintf(ctxt, " ( )");
	DPSPrintf(ctxt, " `\n");

	DPSPrintf(ctxt, "%%%%BeginDocument: ");
	if (filename)
		DPSPrintf(ctxt, "%s", filename);
	DPSPrintf(ctxt,"\n");
}

void  WriteEpsfIllustratorInclude(char *name)
{
	DPSContext	ctxt;

	ctxt = DPSGetCurrentContext();
	DPSPrintf(ctxt, "%%%%IncludeFile: ");
	if (name)
		DPSPrintf(ctxt, "%s", name);
	DPSPrintf(ctxt, "\n");
}

void  WriteEpsfIllustratorEnd()
{
	DPSContext	ctxt;

	ctxt = DPSGetCurrentContext();
	DPSPrintf(ctxt, "%%%%EndDocument\n");
	DPSPrintf(ctxt, "~\n\n");
}

void  WriteEpsfResource(id list, int i, int j)
{
	int			k, count,
				linelen, datalen, commentlen;

	char			comment[EPSF_MAXCOMMENTSUB];

	NXAtom		data;

	DPSContext	ctxt;

	count = [list  count];
	if (count)
	{
		ctxt = DPSGetCurrentContext();
		GetDocumentComment(comment, i, j);
		DPSPrintf(ctxt, comment);

		linelen = commentlen = strlen(comment);
		for (k = 0; k < count; k++)
		{
			data = (NXAtom) [list  objectAt:k];
			datalen = strlen(data);
			if (datalen + linelen > EPSF_MAXCOMMENT)
			{
				/* Prevents an infinite loop for a single large datalen. */
				if (commentlen != linelen)
				{
					DPSPrintf(ctxt, "\n%%%%+");
					linelen = commentlen = 3;
				}
			}

			DPSPrintf(ctxt, " %s", data);
			linelen += datalen;
		}

		DPSPrintf(ctxt, "\n");
	}
}

static char GetBlackWhiteAt(NXBitmapImageRep *image, int x, int y)
{
	int 			bps, spp, amask;

	unsigned char		gray;

	spp = [image samplesPerPixel];
	bps = [image bitsPerSample];

	amask = (1 << bps) - 1;	// 1, 3, 15, 255 for bps = 1, 2, 4, 8
	if ([image isPlanar])
	{
		int pixel = x * bps;
		int byteLoc = [image bytesPerRow] * y + (pixel >> 3);
		int bitLoc = pixel & 7;
	    	gray = ((*([image  data]+byteLoc)) >> (8 - bitLoc - bps)) & amask;
	}
	else
	{
		unsigned char *byteLoc = [image  data] + [image bytesPerRow] * y;
		int bitLoc = x * bps * spp;
		gray = ((byteLoc[bitLoc >> 3]) >> (8 - (bitLoc & 7) - bps)) & amask;
	}

	return (gray >> 1 ? 0 : 1);
}

void WriteEpsfPreview(id  image)
{
	int			byte, orbyte;

	int			i, j, width, height,
				pos, bytes;

	DPSContext	ctxt;

	if (image)
	{
		ctxt = DPSGetCurrentContext();

		width = [image  pixelsWide];
		height = [image  pixelsHigh];
		DPSPrintf(ctxt, "%%%%BeginPreview: %d %d %d %d",
				width, height, 1, (((width-1)/8)/MAX_BYTES + 1) *height);
		for (i = 0; i < height; i++)
		{
			DPSPrintf(ctxt, "\n%% ");
			pos = 7;
			byte = 0;
			bytes = 0;
			for (j = 0; j < width; j++, pos--)
			{
				orbyte = GetBlackWhiteAt(image, j, i) << pos;
				byte = byte | orbyte;
				if (pos == 0 || j == width - 1)
				{
					bytes++;
					if (bytes > MAX_BYTES)
					{
						bytes = 0;
						DPSPrintf(ctxt, "\n%% ");
					}
					DPSPrintf(ctxt, "%02x", byte);
					pos = 8;
					byte = 0;
				}
			}
		}
		DPSPrintf(ctxt, "\n%%%%EndPreview\n\n");
	}
}

/*
*	This does nothing in the way of error recovery. It is used to demonstrate the
*	problems on not using a separate context. Eps files with errors can potentially
*	harm the application's context. The recommended approach is to use
*	the NXEPSImageRep object to image the file.
*/
int  WriteEpsf(const char *name, NXRect *bounds, NXRect *boundsOrig)
{
	BOOL		trace;

	int			error, len, maxlen;

	char			*data;

	NXStream	*stream;

	DPSContext	ctxt;

	trace = [NXApp  tracingFlag];
	error = EPSF_OK;
	stream = NXMapFile(name, NX_READONLY);
	if (stream)
	{
		NXGetMemoryBuffer(stream, &data, &len, &maxlen);
		if (data && len)
		{
			ctxt = DPSGetCurrentContext();
			error = EPSF_WRITEERROR;
			NX_DURING
				DPSTraceContext(ctxt, trace);

				PSWBeginEpsf();
				PSWSetEpsf(bounds->origin.x, bounds->origin.y, bounds->size.width,
					bounds->size.height, boundsOrig->origin.x, boundsOrig->origin.y,
					boundsOrig->size.width, boundsOrig->size.height, 0);

				DPSPrintf(ctxt, "%%%%BeginDocument: %s\n", name);
				DPSPrintf(ctxt, "%%%% ...Included EPS file here... \n");

				DPSTraceContext(ctxt, NO);
				DPSWritePostScript(ctxt, data, len);
				DPSTraceContext(ctxt, trace);

				DPSPrintf(ctxt, "%%%%EndDocument\n");

				PSWEndEpsf();
				DPSTraceContext(ctxt, NO);

				DPSFlushContext(ctxt);

				error = EPSF_OK;
			NX_HANDLER
				if (NXLocalHandler.code == dps_err_ps)
					LogEpsfError(&NXLocalHandler);
				else
					NX_RERAISE();
			NX_ENDHANDLER
		}
	}

	return error;
}
